package com.lgy.ms.service.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Maps;
import com.lgy.ms.api.business.IAppSysServiceProviderService;
import com.lgy.ms.api.other.IAsyncService;
import com.lgy.ms.api.other.ICacheClient;
import com.lgy.ms.core.bean.RequestParams;
import com.lgy.ms.core.bean.ResponseParams;
import com.lgy.ms.core.bean.SessionInfo;
import com.lgy.ms.core.constant.CacheConstant;
import com.lgy.ms.core.constant.Constants;
import com.lgy.ms.core.constant.RespCode;
import com.lgy.ms.core.domain.ServiceLog;
import com.lgy.ms.core.domain.SysServiceProvider;
import com.lgy.ms.core.dto.AppSessionDTO;
import com.lgy.ms.core.enums.CommonEnum;
import com.lgy.ms.core.exception.AppException;
import com.lgy.ms.core.exception.BusinessException;
import com.lgy.ms.core.utils.*;
import com.lgy.ms.service.SpringContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

/**
 * @author Gylin
 */
@Controller
@RequestMapping("/web")
public class WebController {

    private Logger logger = LoggerFactory.getLogger(WebController.class);

    @Resource
    private ICacheClient cacheClient;
    @Resource
    private IAsyncService asyncService;
    @Resource
    private IAppSysServiceProviderService sysServiceProviderService;

    @RequestMapping(value = "/unifyapi", method = RequestMethod.POST)
    public void receive(HttpServletRequest request, HttpServletResponse response) {
        InputStream inputStream = null;
        ResponseParams<Object> responseParams = new ResponseParams<>();
        RequestParams requestParams = new RequestParams();
        String requestParamsStr = "";

        ServiceLog serviceLog = null;
        long startTimeReq = System.currentTimeMillis();
        String reqNo = SysHelper.getUUID("REQ_", 32);

        try {
            inputStream = request.getInputStream();
            String requestMessage = StreamUtil.readInputStream(inputStream);
            Map<String, Object> map = doDealMessage(requestMessage);
            requestParams = (RequestParams) map.get("requestParams");

            // 日志处理
            serviceLog = new ServiceLog();
            serviceLog.setId(reqNo);
            serviceLog.setCreateTime(DateHelper.getCurrentDate());
            serviceLog.setRequestParams(requestMessage);
            serviceLog.setDeviceId(requestParams.getDeviceId());
            serviceLog.setServiceId(requestParams.getServiceId());
            serviceLog.setTermType(requestParams.getTermType());
            serviceLog.setSessionId(requestParams.getSessionId());
            serviceLog.setRequestParams(JSON.toJSONString(requestParams));
            requestParams.setReqNo(reqNo);
            logger.info(">>> req[{}]-ip[{}]-host[{}]-port[{}]", reqNo, request.getRemoteAddr(), request.getRemoteHost(), request.getRemotePort());
            logger.info(">>> req[{}]-requestMsg[{}]", reqNo, serviceLog.getRequestParams());

            if (StringUtil.isNotEmpty(requestParams.getServiceId())) {
                // 完成代理
                SysServiceProvider sysServiceProvider = sysServiceProviderService.queryById(requestParams.getServiceId());
                if (sysServiceProvider != null) {
                    Map<String, Object> externalMap = createUrl(request, requestParams.getExtenalMap());
                    requestParams.setExtenalMap(externalMap);

                    Object obj = SpringContextUtil.getBean(sysServiceProvider.getBeanName());

                    // 校验session
                    boolean sessionCheck = true;
                    if (CommonEnum.Yes.value.equals(sysServiceProvider.getIsCheckSessionid())) {
                        String sessionId = requestParams.getSessionId();
                        if (StringUtil.isEmpty(sessionId)) {
                            sessionCheck = false;
                            responseParams.setRespCode(RespCode.ERR_SESSION_MISS);
                            responseParams.setRespMsg(RespCode.ERR_SESSION_MISS_MS);
                        } else {
                            AppSessionDTO dto = (AppSessionDTO) cacheClient.get(sessionId);
                            if (dto == null) {
                                sessionCheck = false;
                                responseParams.setRespCode(RespCode.ERR_NO_APPSESSIONDTO);
                                responseParams.setRespMsg(RespCode.ERR_NO_APPSESSIONDTO_MS);
                            } else {
                                // 系统管理用户校验即可
                                if (dto.isSys()) {
                                    SessionInfo sessionInfo = (SessionInfo) cacheClient.get(CacheConstant.PREFIX_SESSIONINFO + dto.getUserId());
                                    if (sessionInfo == null) {
                                        sessionCheck = false;
                                        responseParams.setRespCode(RespCode.ERR_SESSION_TIMEOUT);
                                        responseParams.setRespMsg(RespCode.ERR_SESSION_TIMEOUT_MS);
                                    }
                                }
                            }
                        }
                    }
                    if (sessionCheck) {
                        Object responseObj = ReflectUtil.invokeMethod(obj, sysServiceProvider.getMethod(), new Object[]{requestParams});
                        if (responseObj instanceof ResponseParams) {
                            responseParams = (ResponseParams<Object>) responseObj;
                        } else {
                            responseParams.setRespCode(RespCode.ERROR_RETURN_TYPE_ILLEGAL);
                            responseParams.setRespMsg(RespCode.ERROR_RETURN_TYPE_ILLEGAL_MS);
                        }
                    }
                } else {
                    // 未知的服务ID返回错误
                    responseParams.setRespCode(RespCode.EROOR_UNKOWN_SERVICEID);
                    responseParams.setRespMsg(RespCode.EROOR_UNKOWN_SERVICEID_MS);
                }
            } else {
                // 服务ID为空需要返回错误
                responseParams.setRespCode(RespCode.ERROR_SERVICE_ID_EMPTY);
                responseParams.setRespMsg(RespCode.ERROR_SERVICE_ID_EMPTY_MS);
            }
        } catch (InvocationTargetException e) {
            Throwable throwable = e.getTargetException();
            if (throwable instanceof RuntimeException) {
                logger.error(e.getMessage(), e);
                RuntimeException realException = (RuntimeException) throwable;
                try {
                    BusinessException businessException = (BusinessException) realException;
                    responseParams.setRespCode(RespCode.ERROR_UNKOWN);
                    responseParams.setRespMsg(businessException.getMessage());
                } catch (Exception e2) {
                    responseParams.setRespCode(RespCode.ERROR_UNKOWN);
                    responseParams.setRespMsg(RespCode.ERROR_UNKOWN_MS);
                }
            } else {
                responseParams.setRespCode(RespCode.ERROR_UNKOWN);
                responseParams.setRespMsg(RespCode.ERROR_UNKOWN_MS);
            }
            logger.error(throwable.getMessage(), throwable);
        } catch (NoSuchBeanDefinitionException e) {
            responseParams.setRespCode(RespCode.EROOR_NO_DEFINED_BEAN);
            responseParams.setRespMsg(RespCode.EROOR_NO_DEFINED_BEAN_MS);
            logger.error(e.getMessage(), e);
        } catch (BusinessException e) {
            responseParams.setRespCode(RespCode.ERROR_UNKOWN);
            responseParams.setRespMsg(e.getMessage());
            logger.error(e.getMessage(), e);
        } catch (AppException e) {
            responseParams.setRespCode(RespCode.ERROR_UNKOWN);
            responseParams.setRespMsg(e.getMessage());
            logger.error(e.getMessage(), e);
        } catch (Exception e) {
            responseParams.setRespCode(RespCode.ERROR_UNKOWN);
            responseParams.setRespMsg(RespCode.ERROR_UNKOWN_MS);
            logger.error(e.getMessage(), e);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                response.setHeader("Content-type", "text/html;charset=UTF-8");
                PrintWriter out = response.getWriter();
                //进行日志记录
                if (serviceLog != null) {
                    long endTimeReq = System.currentTimeMillis();
                    serviceLog.setResponseParams(JSONObject.toJSONString(responseParams, SerializerFeature.DisableCircularReferenceDetect));
                    serviceLog.setDuration((int) (endTimeReq - startTimeReq));
                    serviceLog.setReturnTime(DateHelper.getCurrentDate());
                    serviceLog.setRespCode(responseParams.getRespCode());
                    String respMsg = responseParams.getRespMsg();
                    if (StringUtil.isNotEmpty(respMsg)) {
                        if (respMsg.length() > 300) {
                            respMsg = respMsg.substring(0, 300);
                        }
                        serviceLog.setRespMsg(respMsg);
                    }
                    logger.info("<<< resp[{}]-requestMsg[{}]-cost[{}]ms", reqNo, serviceLog.getResponseParams(), serviceLog.getDuration());

                    // 异步存日志
                    asyncService.saveServiceLog(serviceLog);
                }

                //返回报文进密码处理
                if (requestParams.getIsEncrypt() == 1) {
                    // 创建响应报文
                    responseParams.setSignType(requestParams.getSignType());
                    responseParams.setEncryptType(requestParams.getEncryptType());
                    responseParams.setTimestamp(DateUtil.getCurrentDateTime());
                    String sign = Signature.createSign(responseParams, requestParams.getAppId(), Constants.appSecrect);
                    responseParams.setSign(sign);
                    requestParamsStr = JSONObject.toJSONString(responseParams.getParam(), SerializerFeature.DisableCircularReferenceDetect);
                    responseParams.setEncryptData(requestParamsStr);
                    // 加密报文
                    logger.info("加密前报文：" + requestParamsStr);
                    String encryptData = SecurityUtil.encrypt(requestParamsStr, requestParams.getEncryptType(), Constants.appSecrect, requestParams.getAppId());
                    logger.info("加密后报文：" + encryptData);
                    responseParams.setEncryptData(encryptData);
                    // 清空明文
                    responseParams.setParam(null);
                }
                String responseMsg = JSON.toJSONString(responseParams, SerializerFeature.DisableCircularReferenceDetect);
                out.write(responseMsg);
                out.close();
            } catch (Exception e1) {
                logger.error("构造响应消息出现异常，异常信息：" + e1.getMessage(), e1);
            }
        }
    }

    private Map<String, Object> createUrl(HttpServletRequest request, Map<String, Object> externalMap) {
        String url = request.getContextPath();
        logger.info("project" + url);
        if (null == externalMap) {
            externalMap = Maps.newHashMap();
        }
        externalMap.put("url", url);
        return externalMap;
    }

    private Map<String, Object> doDealMessage(String requestMessage) throws Exception {
        Map<String, Object> result = Maps.newHashMap();
        if (StringUtil.isEmpty(requestMessage)) {
            throw new BusinessException(RespCode.ERROR_REQUEST_MSG_EMPTY, RespCode.ERROR_REQUEST_MSG_EMPTY_MS);
        }
        RequestParams requestParams = JSON.parseObject(requestMessage, RequestParams.class);
        if (requestParams == null) {
            throw new BusinessException(RespCode.ERROR_REQUEST_MSG_ILLEGAL, RespCode.ERROR_REQUEST_MSG_ILLEGAL_MS);
        }
        // 获取appid、APPSecret并判断是否为空
        String appId = requestParams.getAppId();
        if (StringUtil.isEmpty(appId)) {
            throw new BusinessException(RespCode.ERROR_APP_ID_EMPTY, RespCode.ERROR_APP_ID_EMPTY_MS);
        }
        String timestamp = requestParams.getTimestamp();
        if (StringUtil.isEmpty(timestamp)) {
            throw new BusinessException(RespCode.ERROR_TIMESTAMP_EMPTY, RespCode.ERROR_TIMESTAMP_EMPTY_MS);
        }
        if (requestParams.getIsEncrypt() == 1) {
            try {
                String encryptData = requestParams.getEncryptData();
                logger.info("请求密文:{}", encryptData);
                String decryptData = SecurityUtil.decrypt(encryptData, requestParams.getEncryptType(), Constants.appSecrect, appId);
                logger.info("请求明文:{}", decryptData);
                requestParams.setParam(JSON.parseObject(decryptData, Object.class));
            } catch (Exception e) {
                logger.error("解密失败:", e);
                throw new BusinessException(RespCode.ERROR_REPORT_ENC_FAIL, RespCode.ERROR_REPORT_ENC_FAIL_MS + ",请求失败");
            }

            String signOld = requestParams.getSign();
            String signNew = Signature.createSign(requestParams, Constants.appSecrect);
            logger.info("报文签名，报文签名值：{}，服务端构造的签名值：{}", signOld, signNew);
            boolean isVerify = signOld.equals(signNew);
            if (!isVerify) {
                throw new BusinessException(RespCode.EROOR_FAIL_VERIFY, RespCode.EROOR_FAIL_VERIFY_MS);
            }
        }
        result.put("requestParams", requestParams);
        return result;
    }
}
